package com.dashuai.modbus.core.master;

import com.dashuai.modbus.param.BatchReadParam;
import com.dashuai.modbus.util.CheckUtil;
import com.serotonin.modbus4j.BatchRead;
import com.serotonin.modbus4j.BatchResults;
import com.serotonin.modbus4j.ModbusMaster;
import com.serotonin.modbus4j.code.DataType;
import com.serotonin.modbus4j.exception.ErrorResponseException;
import com.serotonin.modbus4j.exception.ModbusInitException;
import com.serotonin.modbus4j.exception.ModbusTransportException;
import com.serotonin.modbus4j.ip.IpParameters;
import com.serotonin.modbus4j.locator.BaseLocator;
import com.serotonin.modbus4j.locator.NumericLocator;
import com.serotonin.modbus4j.msg.*;
import lombok.extern.slf4j.Slf4j;

import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
 * <p>
 *
 * </p>
 *
 * @author QM
 * @date 2023/7/28
 */
@Slf4j
public class ModbusTCPMaster implements ModbusBaseTCPMaster {


    // 是否有必要考虑做一个可以切换 defaultModbusTCPMaster 的功能，没必要
    private final ModbusMaster defaultModbusTCPMaster;

    private final LinkedHashMap<String, ModbusMaster> masterHashMap = new LinkedHashMap<>();


    public ModbusTCPMaster(String defaultIp, int defaultPort , List<String> ips, List<Integer> ports) throws ModbusInitException {

        defaultModbusTCPMaster = init(defaultIp, defaultPort);

        // 将默认的master也加入到集合中
        final String defaultKey = defaultIp + "_" + defaultPort;
        masterHashMap.put(defaultKey, defaultModbusTCPMaster);

        for (int i = 0; i < ips.size(); i++) {

            final String ip = ips.get(i);
            final Integer port = ports.get(i);
            String key = ip + "_" + port;

            final ModbusMaster modbusTCPMaster = init(ip, port);
            masterHashMap.put(key, modbusTCPMaster);
        }

        log.info("The modbus tcp master is initialized successfully");
    }


    public ModbusMaster init(String ip, Integer port) throws ModbusInitException {

        IpParameters params = new IpParameters();
        params.setHost(ip);
        params.setPort(port);
        ModbusMaster modbusTCPMaster = modbusFactory.createTcpMaster(params, false);
        try {
            modbusTCPMaster.setTimeout(3000);
            modbusTCPMaster.setRetries(0);
            modbusTCPMaster.init();

        } catch (ModbusInitException e) {
            log.error("The modbus tcp master is initialized failure", e);
            throw new ModbusInitException("The modbus tcp master is initialized failure");
        }

        return modbusTCPMaster;
    }


    private ModbusMaster getModbusMaster(String ip, int port) throws ModbusInitException {

        String key = ip + "_" + port;
        final ModbusMaster modbusMaster = masterHashMap.get(key);
        if (modbusMaster == null) {
            log.error("The ip address or port number is incorrect，IP:{}，PORT:{}", ip, port);
            throw new ModbusInitException("The ip address or port number is incorrect");
        }

        return modbusMaster;
    }


    private void checkParameter(String ip, int port, int slaveId, int address) throws ModbusInitException, ModbusTransportException {

        checkSlaveIdAndAddress(slaveId, address);
        CheckUtil.checkIPAndPort(ip, port);
    }


    @Override
    public Boolean readCoilStatus(int slaveId, int offset) throws ModbusTransportException, ErrorResponseException {

        checkSlaveIdAndAddress(slaveId, offset);
        BaseLocator<Boolean> loc = BaseLocator.coilStatus(slaveId, offset);

        return defaultModbusTCPMaster.getValue(loc);
    }


    @Override
    public Boolean readCoilStatus(String ip, int port, int slaveId, int offset) throws ModbusTransportException, ErrorResponseException, ModbusInitException {

        checkParameter(ip, port, slaveId, offset);
        final ModbusMaster modbusMaster = getModbusMaster(ip, port);
        BaseLocator<Boolean> loc = BaseLocator.coilStatus(slaveId, offset);

        return modbusMaster.getValue(loc);
    }


    @Override
    public Boolean readInputStatus(int slaveId, int offset) throws ModbusTransportException, ErrorResponseException {

        checkSlaveIdAndAddress(slaveId, offset);
        BaseLocator<Boolean> loc = BaseLocator.inputStatus(slaveId, offset);

        return defaultModbusTCPMaster.getValue(loc);
    }


    @Override
    public Boolean readInputStatus(String ip, int port, int slaveId, int offset) throws ModbusTransportException, ErrorResponseException, ModbusInitException {

        checkParameter(ip, port, slaveId, offset);
        final ModbusMaster modbusMaster = getModbusMaster(ip, port);
        BaseLocator<Boolean> loc = BaseLocator.inputStatus(slaveId, offset);

        return modbusMaster.getValue(loc);
    }


    @Override
    public Number readHoldingRegister(int slaveId, int offset, int dataType) throws ModbusTransportException, ErrorResponseException {

        checkSlaveIdAndAddress(slaveId, offset);
        // TODO 做完批量读取测试一下这个 BaseLocator 的泛型能否修改成对应的数据类型
        final BaseLocator<Number> loc = NumericLocator.holdingRegister(slaveId, offset, dataType);

        return defaultModbusTCPMaster.getValue(loc);
    }


    @Override
    public <T> T readHoldingRegister(int slaveId, int offset, int dataType, Class<T> returnType) throws ModbusTransportException, ErrorResponseException {

        checkSlaveIdAndAddress(slaveId, offset);
        checkDataType(dataType, returnType);

        final BaseLocator<Number> loc = NumericLocator.holdingRegister(slaveId, offset, dataType);

        final Number value = defaultModbusTCPMaster.getValue(loc);

        return returnType.cast(value);
    }


    @Override
    public Number readHoldingRegister(String ip, int port, int slaveId, int offset, int dataType) throws ModbusTransportException, ErrorResponseException, ModbusInitException {

        checkParameter(ip, port, slaveId, offset);
        final ModbusMaster modbusMaster = getModbusMaster(ip, port);
        final BaseLocator<Number> loc = NumericLocator.holdingRegister(slaveId, offset, dataType);

        return modbusMaster.getValue(loc);
    }


    @Override
    public <T> T readHoldingRegister(String ip, int port, int slaveId, int offset, int dataType, Class<T> returnType) throws ModbusTransportException, ErrorResponseException, ModbusInitException {

        checkParameter(ip, port, slaveId, offset);
        checkDataType(dataType, returnType);

        final ModbusMaster modbusMaster = getModbusMaster(ip, port);
        final BaseLocator<Number> loc = NumericLocator.holdingRegister(slaveId, offset, dataType);

        final Number value = modbusMaster.getValue(loc);

        return returnType.cast(value);
    }


    @Override
    public Number readInputRegisters(int slaveId, int offset, int dataType) throws ModbusTransportException, ErrorResponseException {

        checkSlaveIdAndAddress(slaveId, offset);
        final BaseLocator<Number> loc = NumericLocator.inputRegister(slaveId, offset, dataType);

        return defaultModbusTCPMaster.getValue(loc);
    }


    @Override
    public <T> T readInputRegisters(int slaveId, int offset, int dataType, Class<T> returnType) throws ModbusTransportException, ErrorResponseException {

        checkSlaveIdAndAddress(slaveId, offset);
        checkDataType(dataType, returnType);

        final BaseLocator<Number> loc = NumericLocator.inputRegister(slaveId, offset, dataType);

        final Number value = defaultModbusTCPMaster.getValue(loc);

        return returnType.cast(value);
    }


    @Override
    public Number readInputRegisters(String ip, int port, int slaveId, int offset, int dataType) throws ModbusTransportException, ErrorResponseException, ModbusInitException {

        checkParameter(ip, port, slaveId, offset);
        final ModbusMaster modbusMaster = getModbusMaster(ip, port);
        final BaseLocator<Number> loc = NumericLocator.inputRegister(slaveId, offset, dataType);

        return modbusMaster.getValue(loc);
    }


    @Override
    public <T> T readInputRegisters(String ip, int port, int slaveId, int offset, int dataType, Class<T> returnType) throws ModbusTransportException, ErrorResponseException, ModbusInitException {

        checkParameter(ip, port, slaveId, offset);
        checkDataType(dataType, returnType);

        final ModbusMaster modbusMaster = getModbusMaster(ip, port);
        final BaseLocator<Number> loc = NumericLocator.inputRegister(slaveId, offset, dataType);

        final Number value = modbusMaster.getValue(loc);

        return returnType.cast(value);
    }

    // ---------------------------以上是可以确认功能码、自定义数据类型；以下是可以确认功能码和数据类型-----------------------------

    @Override
    public Short read03Short(int slaveId, int offset) throws ModbusTransportException, ErrorResponseException {

        checkSlaveIdAndAddress(slaveId, offset);
        final BaseLocator<Number> loc = NumericLocator.holdingRegister(slaveId, offset, DataType.TWO_BYTE_INT_SIGNED);

        final Number value = defaultModbusTCPMaster.getValue(loc);

        return value.shortValue();
    }


    @Override
    public Short read03Short(String ip, int port, int slaveId, int offset) throws ModbusTransportException, ErrorResponseException, ModbusInitException {

        checkParameter(ip, port, slaveId, offset);
        final ModbusMaster modbusMaster = getModbusMaster(ip, port);
        final BaseLocator<Number> loc = NumericLocator.holdingRegister(slaveId, offset, DataType.TWO_BYTE_INT_SIGNED);

        final Number value = modbusMaster.getValue(loc);

        return value.shortValue();
    }


    @Override
    public Short read03UnsignedShort(int slaveId, int offset) throws ModbusTransportException, ErrorResponseException {

        checkSlaveIdAndAddress(slaveId, offset);
        final BaseLocator<Number> loc = NumericLocator.holdingRegister(slaveId, offset, DataType.TWO_BYTE_INT_UNSIGNED);

        final Number value = defaultModbusTCPMaster.getValue(loc);

        return value.shortValue();
    }


    @Override
    public Short read03UnsignedShort(String ip, int port, int slaveId, int offset) throws ModbusTransportException, ErrorResponseException, ModbusInitException {

        checkParameter(ip, port, slaveId, offset);
        final ModbusMaster modbusMaster = getModbusMaster(ip, port);
        final BaseLocator<Number> loc = NumericLocator.holdingRegister(slaveId, offset, DataType.TWO_BYTE_INT_UNSIGNED);

        final Number value = modbusMaster.getValue(loc);

        return value.shortValue();
    }


    @Override
    public Integer read03Int(int slaveId, int offset) throws ErrorResponseException, ModbusTransportException {

        checkSlaveIdAndAddress(slaveId, offset);
        final BaseLocator<Number> loc = NumericLocator.holdingRegister(slaveId, offset, DataType.FOUR_BYTE_INT_SIGNED);

        final Number value = defaultModbusTCPMaster.getValue(loc);

        return value.intValue();
    }


    @Override
    public Integer read03Int(String ip, int port, int slaveId, int offset) throws ErrorResponseException, ModbusTransportException, ModbusInitException {

        checkParameter(ip, port, slaveId, offset);
        final ModbusMaster modbusMaster = getModbusMaster(ip, port);
        final BaseLocator<Number> loc = NumericLocator.holdingRegister(slaveId, offset, DataType.FOUR_BYTE_INT_SIGNED);

        final Number value = modbusMaster.getValue(loc);

        return value.intValue();
    }


    @Override
    public Integer read03IntInverse(int slaveId, int offset) throws ErrorResponseException, ModbusTransportException {

        checkSlaveIdAndAddress(slaveId, offset);
        final BaseLocator<Number> loc = NumericLocator.holdingRegister(slaveId, offset, DataType.FOUR_BYTE_INT_SIGNED_SWAPPED);

        final Number value = defaultModbusTCPMaster.getValue(loc);

        return value.intValue();
    }


    @Override
    public Integer read03IntInverse(String ip, int port, int slaveId, int offset) throws ErrorResponseException, ModbusTransportException, ModbusInitException {

        checkParameter(ip, port, slaveId, offset);
        final ModbusMaster modbusMaster = getModbusMaster(ip, port);
        final BaseLocator<Number> loc = NumericLocator.holdingRegister(slaveId, offset, DataType.FOUR_BYTE_INT_SIGNED_SWAPPED);

        final Number value = modbusMaster.getValue(loc);

        return value.intValue();
    }


    @Override
    public Integer read03IntUnsigned(int slaveId, int offset) throws ErrorResponseException, ModbusTransportException {

        checkSlaveIdAndAddress(slaveId, offset);
        final BaseLocator<Number> loc = NumericLocator.holdingRegister(slaveId, offset, DataType.FOUR_BYTE_INT_UNSIGNED);

        final Number value = defaultModbusTCPMaster.getValue(loc);

        return value.intValue();
    }


    @Override
    public Integer read03IntUnsigned(String ip, int port, int slaveId, int offset) throws ErrorResponseException, ModbusTransportException, ModbusInitException {

        checkParameter(ip, port, slaveId, offset);
        final ModbusMaster modbusMaster = getModbusMaster(ip, port);
        final BaseLocator<Number> loc = NumericLocator.holdingRegister(slaveId, offset, DataType.FOUR_BYTE_INT_UNSIGNED);

        final Number value = modbusMaster.getValue(loc);

        return value.intValue();
    }


    @Override
    public Integer read03IntUnsignedInverse(int slaveId, int offset) throws ErrorResponseException, ModbusTransportException {

        checkSlaveIdAndAddress(slaveId, offset);
        final BaseLocator<Number> loc = NumericLocator.holdingRegister(slaveId, offset, DataType.FOUR_BYTE_INT_UNSIGNED_SWAPPED);

        final Number value = defaultModbusTCPMaster.getValue(loc);

        return value.intValue();
    }


    @Override
    public Integer read03IntUnsignedInverse(String ip, int port, int slaveId, int offset) throws ErrorResponseException, ModbusTransportException, ModbusInitException {

        checkParameter(ip, port, slaveId, offset);
        final ModbusMaster modbusMaster = getModbusMaster(ip, port);
        final BaseLocator<Number> loc = NumericLocator.holdingRegister(slaveId, offset, DataType.FOUR_BYTE_INT_UNSIGNED_SWAPPED);

        final Number value = modbusMaster.getValue(loc);

        return value.intValue();
    }


    @Override
    public Long read03Long(int slaveId, int offset) throws ErrorResponseException, ModbusTransportException {

        checkSlaveIdAndAddress(slaveId, offset);
        final BaseLocator<Number> loc = NumericLocator.holdingRegister(slaveId, offset, DataType.EIGHT_BYTE_INT_SIGNED);

        final Number value = defaultModbusTCPMaster.getValue(loc);

        return value.longValue();
    }


    @Override
    public Long read03Long(String ip, int port, int slaveId, int offset) throws ErrorResponseException, ModbusTransportException, ModbusInitException {

        checkParameter(ip, port, slaveId, offset);
        final ModbusMaster modbusMaster = getModbusMaster(ip, port);
        final BaseLocator<Number> loc = NumericLocator.holdingRegister(slaveId, offset, DataType.EIGHT_BYTE_INT_SIGNED);

        final Number value = modbusMaster.getValue(loc);

        return value.longValue();
    }


    @Override
    public Long read03LongInverse(int slaveId, int offset) throws ErrorResponseException, ModbusTransportException {

        checkSlaveIdAndAddress(slaveId, offset);
        final BaseLocator<Number> loc = NumericLocator.holdingRegister(slaveId, offset, DataType.EIGHT_BYTE_INT_SIGNED_SWAPPED);

        final Number value = defaultModbusTCPMaster.getValue(loc);

        return value.longValue();
    }


    @Override
    public Long read03LongInverse(String ip, int port, int slaveId, int offset) throws ErrorResponseException, ModbusTransportException, ModbusInitException {

        checkParameter(ip, port, slaveId, offset);
        final ModbusMaster modbusMaster = getModbusMaster(ip, port);
        final BaseLocator<Number> loc = NumericLocator.holdingRegister(slaveId, offset, DataType.EIGHT_BYTE_INT_SIGNED_SWAPPED);

        final Number value = modbusMaster.getValue(loc);

        return value.longValue();
    }


    @Override
    public Long read03LongUnsigned(int slaveId, int offset) throws ErrorResponseException, ModbusTransportException {

        checkSlaveIdAndAddress(slaveId, offset);
        final BaseLocator<Number> loc = NumericLocator.holdingRegister(slaveId, offset, DataType.EIGHT_BYTE_INT_UNSIGNED);

        final Number value = defaultModbusTCPMaster.getValue(loc);

        return value.longValue();
    }


    @Override
    public Long read03LongUnsigned(String ip, int port, int slaveId, int offset) throws ErrorResponseException, ModbusTransportException, ModbusInitException {

        checkParameter(ip, port, slaveId, offset);
        final ModbusMaster modbusMaster = getModbusMaster(ip, port);
        final BaseLocator<Number> loc = NumericLocator.holdingRegister(slaveId, offset, DataType.EIGHT_BYTE_INT_UNSIGNED);

        final Number value = modbusMaster.getValue(loc);

        return value.longValue();
    }


    @Override
    public Long read03LongUnsignedInverse(int slaveId, int offset) throws ErrorResponseException, ModbusTransportException {

        checkSlaveIdAndAddress(slaveId, offset);
        final BaseLocator<Number> loc = NumericLocator.holdingRegister(slaveId, offset, DataType.EIGHT_BYTE_INT_UNSIGNED_SWAPPED);

        final Number value = defaultModbusTCPMaster.getValue(loc);

        return value.longValue();
    }


    @Override
    public Long read03LongUnsignedInverse(String ip, int port, int slaveId, int offset) throws ErrorResponseException, ModbusTransportException, ModbusInitException {

        checkParameter(ip, port, slaveId, offset);
        final ModbusMaster modbusMaster = getModbusMaster(ip, port);
        final BaseLocator<Number> loc = NumericLocator.holdingRegister(slaveId, offset, DataType.EIGHT_BYTE_INT_UNSIGNED_SWAPPED);

        final Number value = modbusMaster.getValue(loc);

        return value.longValue();
    }


    @Override
    public Float read03FloatABCD(int slaveId, int offset) throws ModbusTransportException, ErrorResponseException {

        checkSlaveIdAndAddress(slaveId, offset);
        final BaseLocator<Number> loc = NumericLocator.holdingRegister(slaveId, offset, DataType.FOUR_BYTE_FLOAT);

        final Number value = defaultModbusTCPMaster.getValue(loc);

        return value.floatValue();
    }


    @Override
    public Float read03FloatABCD(String ip, int port, int slaveId, int offset) throws ModbusTransportException, ErrorResponseException, ModbusInitException {

        checkParameter(ip, port, slaveId, offset);
        final ModbusMaster modbusMaster = getModbusMaster(ip, port);
        final BaseLocator<Number> loc = NumericLocator.holdingRegister(slaveId, offset, DataType.FOUR_BYTE_FLOAT);

        final Number value = modbusMaster.getValue(loc);

        return value.floatValue();
    }


    @Override
    public Float read03Float(int slaveId, int offset) throws ModbusTransportException, ErrorResponseException {

        return read03FloatABCD(slaveId, offset);
    }


    @Override
    public Float read03Float(String ip, int port, int slaveId, int offset) throws ModbusTransportException, ErrorResponseException, ModbusInitException {

        return read03FloatABCD(ip, port, slaveId, offset);
    }


    @Override
    public Float read03FloatCDAB(int slaveId, int offset) throws ModbusTransportException, ErrorResponseException {

        checkSlaveIdAndAddress(slaveId, offset);
        final BaseLocator<Number> loc = NumericLocator.holdingRegister(slaveId, offset, DataType.FOUR_BYTE_FLOAT_SWAPPED);

        final Number value = defaultModbusTCPMaster.getValue(loc);

        return value.floatValue();
    }


    @Override
    public Float read03FloatCDAB(String ip, int port, int slaveId, int offset) throws ModbusTransportException, ErrorResponseException, ModbusInitException {

        checkParameter(ip, port, slaveId, offset);
        final ModbusMaster modbusMaster = getModbusMaster(ip, port);
        final BaseLocator<Number> loc = NumericLocator.holdingRegister(slaveId, offset, DataType.FOUR_BYTE_FLOAT_SWAPPED);

        final Number value = modbusMaster.getValue(loc);

        return value.floatValue();
    }


    @Override
    public Float read03FloatInverse(int slaveId, int offset) throws ModbusTransportException, ErrorResponseException {

        return read03FloatCDAB(slaveId, offset);
    }


    public Float read03FloatInverse(String ip, int port, int slaveId, int offset) throws ModbusTransportException, ErrorResponseException, ModbusInitException {

        return read03FloatCDAB(ip, port, slaveId, offset);
    }


    @Override
    public Double read03DoubleABCDEFGH(int slaveId, int offset) throws ErrorResponseException, ModbusTransportException {

        checkSlaveIdAndAddress(slaveId, offset);
        final BaseLocator<Number> loc = NumericLocator.holdingRegister(slaveId, offset, DataType.EIGHT_BYTE_FLOAT);

        final Number value = defaultModbusTCPMaster.getValue(loc);

        return value.doubleValue();
    }


    @Override
    public Double read03DoubleABCDEFGH(String ip, int port, int slaveId, int offset) throws ErrorResponseException, ModbusTransportException, ModbusInitException {

        checkParameter(ip, port, slaveId, offset);
        final ModbusMaster modbusMaster = getModbusMaster(ip, port);
        final BaseLocator<Number> loc = NumericLocator.holdingRegister(slaveId, offset, DataType.EIGHT_BYTE_FLOAT);

        final Number value = modbusMaster.getValue(loc);

        return value.doubleValue();
    }


    @Override
    public Double read03Double(int slaveId, int offset) throws ModbusTransportException, ErrorResponseException {

        return read03DoubleABCDEFGH(slaveId, offset);
    }


    @Override
    public Double read03Double(String ip, int port, int slaveId, int offset) throws ModbusTransportException, ErrorResponseException, ModbusInitException {

        return read03DoubleABCDEFGH(ip, port, slaveId, offset);
    }


    @Override
    public Double read03DoubleGHEFCDAB(int slaveId, int offset) throws ErrorResponseException, ModbusTransportException {

        checkSlaveIdAndAddress(slaveId, offset);
        final BaseLocator<Number> loc = NumericLocator.holdingRegister(slaveId, offset, DataType.EIGHT_BYTE_FLOAT_SWAPPED);

        final Number value = defaultModbusTCPMaster.getValue(loc);

        return value.doubleValue();
    }


    @Override
    public Double read03DoubleGHEFCDAB(String ip, int port, int slaveId, int offset) throws ErrorResponseException, ModbusTransportException, ModbusInitException {

        checkParameter(ip, port, slaveId, offset);
        final ModbusMaster modbusMaster = getModbusMaster(ip, port);
        final BaseLocator<Number> loc = NumericLocator.holdingRegister(slaveId, offset, DataType.EIGHT_BYTE_FLOAT_SWAPPED);

        final Number value = modbusMaster.getValue(loc);

        return value.doubleValue();
    }


    @Override
    public Double read03DoubleInverse(int slaveId, int offset) throws ModbusTransportException, ErrorResponseException {

        return read03DoubleGHEFCDAB(slaveId, offset);
    }


    @Override
    public Double read03DoubleInverse(String ip, int port, int slaveId, int offset) throws ModbusTransportException, ErrorResponseException, ModbusInitException {

        return read03DoubleGHEFCDAB(ip, port, slaveId, offset);
    }


    @Override
    public <T> Map<Integer, T> batchRead(int slaveId, List<Integer> offsetList, int dataType, Class<T> returnType)
            throws ErrorResponseException, ModbusTransportException {

        for (Integer offset : offsetList) {
            checkSlaveIdAndAddress(slaveId, offset);
        }
        checkDataType(dataType, returnType);

        return executeBatchRead(slaveId, offsetList, dataType, returnType, defaultModbusTCPMaster);
    }


    @Override
    public <T> Map<Integer, T> batchRead(String ip, int port, int slaveId, List<Integer> offsetList, int dataType, Class<T> returnType)
            throws ErrorResponseException, ModbusTransportException, ModbusInitException {

        for (Integer offset : offsetList) {
            checkParameter(ip, port, slaveId, offset);
        }
        checkDataType(dataType, returnType);

        final ModbusMaster modbusMaster = getModbusMaster(ip, port);

        return executeBatchRead(slaveId, offsetList, dataType, returnType, modbusMaster);
    }


    private <T> Map<Integer, T> executeBatchRead(int slaveId, List<Integer> offsetList, int dataType, Class<T> returnType, ModbusMaster modbusMaster)
            throws ErrorResponseException, ModbusTransportException {

        // 创建批量读取对象
        BatchRead<Integer> batch = new BatchRead<>();
        for (int i = 0; i < offsetList.size(); i++) {
            batch.addLocator(i, BaseLocator.holdingRegister(slaveId, offsetList.get(i), dataType));
        }

        // 发送请求并处理结果
        BatchResults<Integer> results = modbusMaster.send(batch);
        Map<Integer, T> resultMap = new LinkedHashMap<>();

        for (int i = 0; i < offsetList.size(); i++) {
            resultMap.put(offsetList.get(i), returnType.cast(results.getValue(i)));
        }

        return resultMap;
    }


    @Override
    public <T> Map<Integer, T> batchRead(List<BatchReadParam> batchReadParamList, int dataType, Class<T> returnType) throws ErrorResponseException, ModbusTransportException {

        for (BatchReadParam batchReadParam : batchReadParamList) {
            checkSlaveIdAndAddress(batchReadParam.getSlaveId(), batchReadParam.getOffset());
        }
        checkDataType(dataType, returnType);

        return executeBatchRead(batchReadParamList, dataType, returnType, defaultModbusTCPMaster);
    }


    @Override
    public <T> Map<Integer, T> batchRead(String ip, int port, List<BatchReadParam> batchReadParamList, int dataType, Class<T> returnType) throws ErrorResponseException, ModbusTransportException, ModbusInitException {

        for (BatchReadParam batchReadParam : batchReadParamList) {
            checkSlaveIdAndAddress(batchReadParam.getSlaveId(), batchReadParam.getOffset());
        }
        checkDataType(dataType, returnType);

        final ModbusMaster modbusMaster = getModbusMaster(ip, port);

        return executeBatchRead(batchReadParamList, dataType, returnType, modbusMaster);
    }


    private <T> Map<Integer, T> executeBatchRead(List<BatchReadParam> batchReadParamList, int dataType, Class<T> returnType, ModbusMaster modbusMaster)
            throws ErrorResponseException, ModbusTransportException {

        // 创建批量读取对象
        BatchRead<Integer> batch = new BatchRead<>();

        final int size = batchReadParamList.size();
        for (int i = 0; i < size; i++) {

            final BatchReadParam batchReadParam = batchReadParamList.get(i);
            batch.addLocator(i, BaseLocator.holdingRegister(batchReadParam.getSlaveId(),
                    batchReadParam.getOffset(), dataType));
        }

        // 发送请求并处理结果
        BatchResults<Integer> results = modbusMaster.send(batch);
        Map<Integer, T> resultMap = new LinkedHashMap<>();

        for (int i = 0; i < size; i++) {
            resultMap.put(batchReadParamList.get(i).getOffset(), returnType.cast(results.getValue(i)));
        }

        return resultMap;
    }


    @Override
    public boolean writeCoil(int slaveId, int writeOffset, boolean writeValue) throws ModbusTransportException {

        // 创建请求
        WriteCoilRequest request = new WriteCoilRequest(slaveId, writeOffset, writeValue);

        WriteCoilResponse response = (WriteCoilResponse) defaultModbusTCPMaster.send(request);

        return !response.isException();
    }


    @Override
    public boolean writeCoil(String ip, int port, int slaveId, int writeOffset, boolean writeValue) throws ModbusTransportException, ModbusInitException {

        // 创建请求
        WriteCoilRequest request = new WriteCoilRequest(slaveId, writeOffset, writeValue);

        final ModbusMaster modbusMaster = getModbusMaster(ip, port);

        WriteCoilResponse response = (WriteCoilResponse) modbusMaster.send(request);

        return !response.isException();
    }


    @Override
    public boolean batchWriteCoil(int slaveId, int startOffset, boolean[] writeValueArr) throws ModbusTransportException {

        WriteCoilsRequest request = new WriteCoilsRequest(slaveId, startOffset, writeValueArr);
        // 发送请求并获取响应对象
        WriteCoilsResponse response = (WriteCoilsResponse) defaultModbusTCPMaster.send(request);

        return !response.isException();
    }


    @Override
    public boolean batchWriteCoil(String ip, int port, int slaveId, int startOffset, boolean[] writeValueArr) throws ModbusTransportException, ModbusInitException {

        WriteCoilsRequest request = new WriteCoilsRequest(slaveId, startOffset, writeValueArr);

        final ModbusMaster modbusMaster = getModbusMaster(ip, port);

        // 发送请求并获取响应对象
        WriteCoilsResponse response = (WriteCoilsResponse) modbusMaster.send(request);

        return !response.isException();
    }


    @Override
    public boolean writeRegister(int slaveId, int writeOffset, short writeValue) throws ModbusTransportException {

        WriteRegisterRequest request = new WriteRegisterRequest(slaveId, writeOffset, writeValue);

        WriteRegisterResponse response = (WriteRegisterResponse) defaultModbusTCPMaster.send(request);

        return !response.isException();
    }


    @Override
    public boolean writeRegister(String ip, int port, int slaveId, int writeOffset, short writeValue) throws ModbusTransportException, ModbusInitException {

        WriteRegisterRequest request = new WriteRegisterRequest(slaveId, writeOffset, writeValue);

        final ModbusMaster modbusMaster = getModbusMaster(ip, port);

        WriteRegisterResponse response = (WriteRegisterResponse) modbusMaster.send(request);

        return !response.isException();
    }


    @Override
    public boolean batchWriteRegister(int slaveId, int startOffset, short[] writeValueArr) throws ModbusTransportException {

        WriteRegistersRequest request = new WriteRegistersRequest(slaveId, startOffset, writeValueArr);

        ModbusResponse response = defaultModbusTCPMaster.send(request);

        return !response.isException();
    }


    @Override
    public boolean batchWriteRegister(String ip, int port, int slaveId, int startOffset, short[] writeValueArr) throws ModbusTransportException, ModbusInitException {

        WriteRegistersRequest request = new WriteRegistersRequest(slaveId, startOffset, writeValueArr);

        final ModbusMaster modbusMaster = getModbusMaster(ip, port);

        ModbusResponse response = modbusMaster.send(request);

        return !response.isException();
    }


    @Override
    public void writeHoldingRegister(int slaveId, int offset, Number value, int dataType) throws ModbusTransportException, ErrorResponseException {

        writeRegisterValue(defaultModbusTCPMaster, slaveId, offset, value, dataType);
    }


    @Override
    public void writeHoldingRegister(String ip, int port, int slaveId, int offset, Number value, int dataType) throws ModbusTransportException, ErrorResponseException, ModbusInitException {

        final ModbusMaster modbusMaster = getModbusMaster(ip, port);

        writeRegisterValue(modbusMaster, slaveId, offset, value, dataType);
    }


    private void writeRegisterValue(ModbusMaster modbusMaster, int slaveId, int offset, Number value, int dataType) throws ModbusTransportException, ErrorResponseException {

        BaseLocator<Number> locator = BaseLocator.holdingRegister(slaveId, offset, dataType);

        modbusMaster.setValue(locator, value);
    }


}
